//
//  SC_AsyncAwait_DemoVC.swift
//  APSwift
//
//  Created by jianbo.pei on 2025/11/14.
//

import UIKit

class SC_AsyncAwait_DemoVC: UIViewController {

    private func JuLog(_ items: Any..., separator: String = " ", terminator: String = "\n") {
        let text = items.map { String(describing: $0) }.joined(separator: separator)
        debugPrint(text + terminator)
    }

    private let doc: String = """
# Swift Concurrency 详解（Async/Await）

## 结构化并发
- 使用 `Task` 创建异步任务，生命周期由父任务管理。
- 使用 `async let` 并发执行多个异步子任务并在使用时等待。
- 使用 `withTaskGroup/withThrowingTaskGroup` 聚合并发子任务结果。

## 取消
- 取消是协作式的：使用 `Task.isCancelled` 或 `Task.checkCancellation()`。
- 多处 await 时应及时检查取消以尽快停止。

## UI 与主线程
- 通过 `await MainActor.run { ... }` 更新 UI。

## Continuation
- 用 `withCheckedContinuation/withCheckedThrowingContinuation` 将回调 API 转为 async。

## AsyncSequence
- 使用 `AsyncStream` 生产异步事件，`for await` 消费。

## 优先级与 DetachedTask
- `TaskPriority` 决定调度倾向；`DetachedTask` 不继承父任务上下文。

## TaskLocal
- 在任务作用域内传递上下文值。

示例函数：
- `example_basic_asyncAwait`
- `example_asyncLet`
- `example_task_group`
- `example_cancellation`
- `example_main_actor_ui_update`
- `example_continuation_bridge`
- `example_async_sequence_stream`
- `example_task_priority_detached`
- `example_task_local`
"""

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground

        let textView = UITextView(frame: view.bounds)
        textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        textView.isEditable = false
        textView.text = doc
        textView.font = UIFont.systemFont(ofSize: 14)
        view.addSubview(textView)

        Task { await self.example_basic_asyncAwait() }
        Task { await self.example_asyncLet() }
        Task { await self.example_main_actor_ui_update() }
    }

    private func example_basic_asyncAwait() async {
        do {
            let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")!
            let (data, _) = try await URLSession.shared.data(from: url)
            let obj = try JSONSerialization.jsonObject(with: data) as? [String: Any]
            JuLog("basic_asyncAwait", obj ?? [:])
        } catch {
            JuLog("basic_asyncAwait_error", error)
        }
    }

    private func example_asyncLet() async {
        @Sendable func getUser() async -> String {
            try? await Task.sleep(nanoseconds: 300_000_000)
            return "user_001"
        }
        @Sendable func getPostsCount() async -> Int {
            try? await Task.sleep(nanoseconds: 200_000_000)
            return 42
        }
        async let u = getUser()
        async let c = getPostsCount()
        let (user, count) = await (u, c)
        JuLog("asyncLet", user, count)
    }

    private func example_task_group() async {
        let result = await withTaskGroup(of: Int.self) { group -> Int in
            for i in 1...5 {
                group.addTask {
                    try? await Task.sleep(nanoseconds: UInt64(i) * 100_000_000)
                    return i
                }
            }
            var sum = 0
            for await v in group { sum += v }
            return sum
        }
        JuLog("task_group_sum", result)
    }

    private func example_cancellation() async {
        let t = Task { () -> Int in
            var x = 0
            for i in 0..<10_000 {
                if Task.isCancelled { return x }
                x += i
                if i % 500 == 0 { try? Task.checkCancellation() }
            }
            return x
        }
        Task { try? await Task.sleep(nanoseconds: 300_000_000); t.cancel() }
        let v = await t.value
        JuLog("cancellation_value", v)
    }

    private func example_main_actor_ui_update() async {
        try? await Task.sleep(nanoseconds: 200_000_000)
        await MainActor.run { self.view.backgroundColor = .systemGroupedBackground }
        JuLog("main_actor_ui_update")
    }

    private func example_continuation_bridge() async {
        func legacyCall(_ completion: @escaping (Result<String, Error>) -> Void) {
            DispatchQueue.global().asyncAfter(deadline: .now() + 0.2) {
                completion(.success("legacy_ok"))
            }
        }
        func modernCall() async throws -> String {
            try await withCheckedThrowingContinuation { cont in
                legacyCall { cont.resume(with: $0) }
            }
        }
        do {
            let s = try await modernCall()
            JuLog("continuation_bridge", s)
        } catch {
            JuLog("continuation_bridge_error", error)
        }
    }

    private func example_async_sequence_stream() async {
        let stream = AsyncStream<Int> { continuation in
            Task {
                for i in 1...5 {
                    continuation.yield(i)
                    try? await Task.sleep(nanoseconds: 100_000_000)
                }
                continuation.finish()
            }
        }
        for await v in stream { JuLog("async_stream", v) }
    }

    private func example_task_priority_detached() async {
        let h1 = Task(priority: .userInitiated) { Task.currentPriority }
        let p1 = await h1.value
        let h2 = Task(priority: .background) { Task.currentPriority }
        let p2 = await h2.value
        let d = Task.detached { Task.currentPriority }
        let p3 = await d.value
        JuLog("priority", p1, p2, p3)
    }

    private func example_task_local() async {
        enum Context { @TaskLocal static var userID: String? }
        Context.$userID.withValue("user_abc") {
            let id = Context.userID ?? "none"
            JuLog("task_local", id)
        }
    }
}
